package com.wmada.toolkit.common.secure;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * @author will.ma
 * @date 2017/3/11
 */
public class RSAUtils {

    private static Logger logger = LoggerFactory.getLogger(RSAUtils.class);

    private static final String ALGORITHM          = "RSA";
    private static final int    DEFAULT_KEY_LENGTH = 1024;

    public static KeyPair generateKeyPair(int keyLength) {
        try {
            KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(ALGORITHM);
            keyPairGen.initialize(keyLength, new SecureRandom());
            return keyPairGen.generateKeyPair();
        } catch (Exception e) {
            logger.error("生成秘钥对失败", e);
            return null;
        }
    }

    public static KeyPair generateKeyPair(int keyLength, byte[] seed) {
        try {
            KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(ALGORITHM);
            keyPairGen.initialize(keyLength, new SecureRandom(seed));
            return keyPairGen.generateKeyPair();
        } catch (Exception e) {
            logger.error("生成秘钥对失败", e);
            return null;
        }
    }

    public static KeyPair generateKeyPair(byte[] seed) {
        return generateKeyPair(DEFAULT_KEY_LENGTH, seed);
    }

    public static KeyPair generateKeyPair() {
        return generateKeyPair(DEFAULT_KEY_LENGTH);
    }

    public static RSAPublicKey loadPublicKey(String keyStr) {
        try {
            byte[]             buffer     = EncodeUtils.base64Decode(keyStr);
            KeyFactory         keyFactory = KeyFactory.getInstance(ALGORITHM);
            X509EncodedKeySpec keySpec    = new X509EncodedKeySpec(buffer);
            return (RSAPublicKey) keyFactory.generatePublic(keySpec);
        } catch (Exception e) {
            logger.error("解析公钥失败", e);
            return null;
        }
    }

    public static RSAPrivateKey loadPrivateKey(String keyStr) {
        try {
            byte[]              buffer     = EncodeUtils.base64Decode(keyStr);
            KeyFactory          keyFactory = KeyFactory.getInstance(ALGORITHM);
            PKCS8EncodedKeySpec keySpec    = new PKCS8EncodedKeySpec(buffer);
            return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
        } catch (Exception e) {
            logger.error("解析私钥失败", e);
            return null;
        }
    }

    public static byte[] encrypt(Cipher cipher, byte[] data) {
        try {
            return cipher.doFinal(data);
        } catch (Exception e) {
            logger.warn("加密信息失败", e);
            return null;
        }
    }

    public static String encrypt(Cipher cipher, String message) {
        try {
            byte[] data = encrypt(cipher, message.getBytes("utf-8"));
            if (data != null) {
                return EncodeUtils.base64Encode(data);
            } else {
                return null;
            }
        } catch (Exception e) {
            logger.warn("加密信息失败", e);
            return null;
        }
    }

    public static byte[] decrypt(Cipher cipher, byte[] data) {
        try {
            return cipher.doFinal(data);
        } catch (IllegalBlockSizeException | BadPaddingException e) {
            logger.warn("解密信息失败", e);
            return null;
        }
    }

    public static String decrypt(Cipher cipher, String message) {
        byte[] data = decrypt(cipher, EncodeUtils.base64Decode(message));
        if (data != null) {
            return new String(data);
        } else {
            return null;
        }
    }

    public static Cipher getEncryptCipher(RSAPublicKey publicKey) {
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            return cipher;
        } catch (Exception e) {
            logger.warn("生成加密Cipher失败", e);
            return null;
        }
    }

    public static Cipher getDecryptCipher(RSAPrivateKey privateKey) {
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return cipher;
        } catch (Exception e) {
            logger.warn("生成解密Cipher失败", e);
            return null;
        }
    }

}
